package camdp;

import graph.Graph;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import xadd.XADD;
import xadd.XADD.ArithExpr;
import xadd.XADD.CompExpr;
import xadd.XADD.DoubleExpr;
import xadd.XADD.ExprDec;

public class ParseCASMDP {
	
	CASMDP _casmdp=null;
	
	public final static ArithExpr ZERO = new DoubleExpr(0d);
	public final static ArithExpr ONE = new DoubleExpr(1d);
	ArrayList<String> CVars = new ArrayList<String>();
	public HashMap<String, Double> _minCVal = new HashMap<String, Double>();
	public HashMap<String, Double> _maxCVal = new HashMap<String, Double>();
	ArrayList<String> BVars = new ArrayList<String>();
	ArrayList<String> NoiseVars = new ArrayList<String>();
	ArrayList<String> IVars = new ArrayList<String>();
	ArrayList<String> AVars = new ArrayList<String>();
	ArrayList<Double> contParam = new ArrayList<Double>(2);
	ArrayList<Integer> constraints = new ArrayList<Integer>();
	BigDecimal discount ;
	Integer iterations ;
	boolean isContAction;
	HashMap<String, SAction> hashmap = new HashMap<String, SAction>();
	
	public ParseCASMDP(CASMDP casmdp,boolean isCont)
	{
		_casmdp = casmdp;
		isContAction = isCont;
	}

	public void buildCASMDP(ArrayList input) {

		if (input == null) {
			System.out.println("Empty input file!");
			System.exit(1);
		}

		Iterator i = input.iterator();
		Object o;

		// Set up variables
		o = i.next();
		if (!(o instanceof String) || !((String) o).equalsIgnoreCase("cvariables")) {
			exit("Missing cvariable declarations: " + o);
		}
		o = i.next();
		CVars = (ArrayList<String>) ((ArrayList) o).clone();		
		o = i.next();
		if (!(o instanceof String) || !((String) o).equalsIgnoreCase("min-values")) {
			exit("Missing min-values declarations: " + o);
		}
		o = i.next();
		for (int index = 0; index < CVars.size(); index++) {
			String var = CVars.get(index);
			String val = ((ArrayList) o).get(index).toString();
			if (!val.trim().equalsIgnoreCase("x")) try {
				double min_val = Double.parseDouble(val);
				_minCVal.put(var, min_val);
			} catch (NumberFormatException nfe) {
				System.out.println("\nIllegal min-value: " + var + " = " + val + " @ index " + index);
				System.exit(1);
			}
		}
		o = i.next();
		if (!(o instanceof String) || !((String) o).equalsIgnoreCase("max-values")) {
			exit("Missing max-values declarations: " + o);
		}
		o = i.next();
		for (int index = 0; index < CVars.size(); index++) {
			String var =CVars.get(index);
			String val = ((ArrayList) o).get(index).toString();
			if (!val.trim().equalsIgnoreCase("x")) try {
				double max_val = Double.parseDouble(val);
				_maxCVal.put(var, max_val);
			} catch (NumberFormatException nfe) {
				System.out.println("\nIllegal max-value: " + var + " = " + val + " @ index " + index);
				System.exit(1);
			}
		}
		o = i.next();
		if (!(o instanceof String) || !((String) o).equalsIgnoreCase("bvariables")) {
			exit("Missing bvariable declarations: " + o);
		}
		o = i.next();
		//System.out.println(o);
		BVars= (ArrayList<String>) ((ArrayList) o).clone();
		for (String var : BVars) {
			_casmdp._context.getVarIndex(_casmdp._context.new BoolDec(var), true);
			_casmdp._context.getVarIndex(_casmdp._context.new BoolDec(var + "'"), true);
		}
		//noise variables
				o = i.next();
				if (!(o instanceof String) || !((String) o).equalsIgnoreCase("nvariables")) {
					exit("Missing nvariable declarations: " + o);
				}
		o = i.next();
		NoiseVars = (ArrayList<String>) ((ArrayList) o).clone();
		
		o = i.next();
		if (!(o instanceof String) || !((String) o).equalsIgnoreCase("ivariables")) {
			exit("Missing ivariable declarations: " + o);
		}
		o = i.next();
		IVars = (ArrayList<String>) ((ArrayList) o).clone();

		
		if (isContAction)
		{
			o = i.next();
			if (!(o instanceof String) || !((String) o).equalsIgnoreCase("avariables")) {
				exit("Missing avariable declarations: " + o);
			}
			o = i.next();
			AVars = (ArrayList<String>) ((ArrayList) o).clone();
		}

		ArrayList<String> contVars = new ArrayList<String>();
		contVars.addAll(CVars);
		if (isContAction) contVars.addAll(AVars);
			
		// Set up actions
		while (true) 
		{
			o = i.next();
			if (!(o instanceof String)
					|| !((String) o).equalsIgnoreCase("action")) {
				break;
			}

			// o == "action" + continuous action
			String aname = (String) i.next();
			if (isContAction)
			{
				o= i.next();
				ArrayList<String> temp=null;
			
				//for more than one continuous parameter for each action
				for (int k=0;k<CVars.size();k++)
				{
					while (!(o.equals(CVars.get(k)+"'")))
						{
							
							temp=(ArrayList<String>) ((ArrayList) o).clone();
							break;
						}
				}
				//if it is a continuous action (has avariables) then it either has bounds in the paranthesis or we add bounds
				if (AVars.size()>0)
					parseActionParam(temp);
			}
			HashMap<String,ArrayList> cpt_map = new HashMap<String,ArrayList>();
			HashMap<String,ArrayList> noise_map = new HashMap<String,ArrayList>();

			// o == "action"
			o = i.next();
			while (!((String) o).equalsIgnoreCase("noise")) {//endaction
				cpt_map.put((String) o, (ArrayList) i.next());
				o = i.next();
			}
			
			if (NoiseVars.size()>0)
			{
				o = i.next();
				while (!((String) o).equalsIgnoreCase("reward"))
				{
					noise_map.put((String) o, (ArrayList) i.next());
					o = i.next();
				}
			}
			else o = i.next();

			// Set up reward
			if (!(o instanceof String) || !((String) o).equalsIgnoreCase("reward")) {
				System.out.println("Missing reward declaration for action: "+aname +" "+ o);
				System.exit(1);
			}
			//new parser format : has + for ANDing rewards
			o=i.next();
			ArrayList reward = (ArrayList) o;
			int _runningSum=0,reward_dd = 0;
			int reward_toGoal = _casmdp._context.buildCanonicalXADD(reward);
			reward_dd = reward_toGoal;
			//new parser format : has + for ANDing rewards
			 o = i.next();
			 while (!((String) o).equalsIgnoreCase("endaction"))
			{
				 
				 int reward_2=0;
				if (((String) o).equalsIgnoreCase("+"))
				{
					o = i.next();
					reward = (ArrayList) o; 
					reward_2 = _casmdp._context.buildCanonicalXADD(reward);
					int T_ZERO = _casmdp._context.getTermNode(ZERO);
					int T_ONE = _casmdp._context.getTermNode(ONE);
					int var=1;
					if (BVars.size()>0)
						var = _casmdp._context.getVarIndex(_casmdp._context.new BoolDec(BVars.get(0)), false);
					int ind_true = _casmdp._context.getINode(var,  T_ZERO,  T_ONE);
					int ind_false = _casmdp._context.getINode(var,  T_ONE,  T_ZERO);
					//int true_half = _context.applyInt(ind_true, reward_toGoal, _context.PROD,-1); // Note: this enforces canonicity so
					//int reward_d = _context.apply(reward_2,reward_toGoal, _context.SUM,-1);
					int false_half = _casmdp._context.applyInt(ind_false, reward_2, _casmdp._context.PROD); // can use applyInt rather than apply
					//reward_dd = _context.applyInt(true_half, false_half, _context.SUM,-1);
					reward_dd = _casmdp._context.applyInt(reward_2, reward_dd, _casmdp._context.SUM);
				}
				o=i.next();
			}
				
			if (reward_dd>0)
				hashmap.put(aname, new SAction(_casmdp, aname, contParam, cpt_map, noise_map,reward_dd,CVars,AVars,BVars));
			else hashmap.put(aname, new SAction(_casmdp, aname, contParam, cpt_map, noise_map, reward_toGoal,CVars,AVars,BVars));
			
				//o=i.next(); // endaction
			
		}

		// Check for constraints declaration (can be multiple)
		constraints =new ArrayList<Integer>();
		while (true) {
			if (!(o instanceof String)
					|| !((String) o).equalsIgnoreCase("constraint")) {
				break;
			}

			o=i.next(); // get dd
			ArrayList next_constraint = (ArrayList) o;
			int next_constraint_dd = _casmdp._context.buildCanonicalXADD(next_constraint);
			constraints.add(next_constraint_dd);
			
			o = i.next(); // get endconstraint
			o = i.next(); // get constraint or discount
		}
		// Read discount and tolerance
		//o = i.next();
		if (!(o instanceof String)
				|| !((String) o).equalsIgnoreCase("discount")) {
			System.out.println("Missing discount declaration: " + o);
			System.exit(1);
		}
		discount = ((BigDecimal) i.next());
		o = i.next();
		if (!(o instanceof String)
				|| !((String) o).equalsIgnoreCase("iterations")) {
			System.out.println("Missing iterations declaration: " + o);
			System.exit(1);
		}
		iterations = (new Integer((String)i.next()));
		
	}
	
	public HashMap<String, Double> get_minCVal() {
		return _minCVal;
	}

	public void set_minCVal(HashMap<String, Double> _minCVal) {
		this._minCVal = _minCVal;
	}

	public HashMap<String, Double> get_maxCVal() {
		return _maxCVal;
	}

	public void set_maxCVal(HashMap<String, Double> _maxCVal) {
		this._maxCVal = _maxCVal;
	}

	
	private void parseActionParam(ArrayList<String> params) {
		//parsing stage 1: 
		//Assume only either both numerical(not non-numerical) bounds are given or none
		int breakpoint =-1;
		for (int i=0;i<AVars.size();i++)
			if (params.contains(AVars.get(i)))
			{
				contParam.add(i*2, new Double(params.get(breakpoint+1)));
				for (int j=breakpoint+1;j<params.size();j++)
					if (params.get(j).equals("^")){
						breakpoint = j;
						break;
					}
					else breakpoint = params.size();
				contParam.add((i*2)+1, new Double(params.get(breakpoint-1)));
			}
			else if (params.size()==0) //no bounds defined, add explicitly
			{
				contParam.add(0, -1000000.0);
				contParam.add(1, 1000000.0);
			}
		
	}
	
	public void exit(String msg) {
		System.out.println(msg);
		System.exit(1);
	}

	public XADD get_context() {
		return _casmdp._context;
	}

	public void set_context(XADD _context) {
		this._casmdp._context = _context;
	}



	public ArrayList<String> getCVars() {
		return CVars;
	}

	public ArrayList<String> getBVars() {
		return BVars;
	}
	
	public ArrayList<String> getAVars() {
		return AVars;
	}

	public ArrayList<String> getIVars() {
		return IVars;
	}

	public ArrayList<Integer> getConstraints() {
		return constraints;
	}

	public BigDecimal getDiscount() {
		return discount;
	}

	public Integer getIterations() {
		return iterations;
	}

	public HashMap<String, SAction> getHashmap() {
		return hashmap;
	}

	public ArrayList<String> getNoiseVars() {
		return NoiseVars;
	}

	public void setNoiseVars(ArrayList<String> noiseVars) {
		NoiseVars = noiseVars;
	}

	
	
	
	
	
}
